/*
 * This file is part of connotea-java.
 *
 * connotea-java is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * connotea-java is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package org.connotea;

import static java.util.Collections.unmodifiableList;
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
import static org.apache.commons.lang.builder.ToStringBuilder.reflectionToString;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.builder.CompareToBuilder;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.restlet.data.Reference;

/**
 * Bookmark entity bean.
 * 
 * @author <a href="mailto:christopher.townson@googlemail.com">Christopher
 *         Townson</a>
 */
public final class Bookmark implements Comparable<Bookmark>, Serializable {

    private static final long serialVersionUID = 1L;

    private Long bookmarkID;

    private Citation citation;

    private Date created;

    private String firstUser;

    private String hash;

    private Reference link;

    private Long postCount;

    private List<String> postedBy = new ArrayList<String>();

    private List<Tag> tags = new ArrayList<Tag>();

    private String title;

    private Date updated;

    /**
     * Default, no-argument constructor.
     */
    public Bookmark() {
        // do nothing
    }

    /**
     * Copy constructor.
     * 
     * @param bookmark the bookmark to copy
     */
    public Bookmark(Bookmark bookmark) {
        setBookmarkID(bookmark.getBookmarkID());
        setCitation(new Citation(bookmark.getCitation()));
        setCreated(bookmark.getCreated());
        setFirstUser(bookmark.getFirstUser());
        setHash(bookmark.getHash());
        setLink(new Reference(bookmark.getLink()));
        setPostCount(bookmark.getPostCount());
        setPostedBy(bookmark.getPostedBy());
        setTags(bookmark.getTags());
        setTitle(bookmark.getTitle());
        setUpdated(bookmark.getUpdated());
    }

    /**
     * Create a Bookmark instance using the supplied reference.
     * 
     * @param link
     */
    public Bookmark(Reference link) {
        setLink(link);
    }

    /**
     * Create a Bookmark instance using the supplied reference and tags.
     * 
     * @param link the link reference
     * @param tags the tags
     */
    public Bookmark(Reference link, List<Tag> tags) {
        setLink(link);
        setTags(tags);
    }

    /**
     * Comparability is based on the Connotea hash of this bookmark.
     * 
     * @see java.lang.Comparable#compareTo(java.lang.Object)
     */
    public int compareTo(Bookmark bookmark) {
        return new CompareToBuilder().append(hash, bookmark.hash)
                .toComparison();
    }

    /**
     * Equality is based on the Connotea hash of this bookmark.
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     * @see #getHash()
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Bookmark)) {
            return false;
        }
        Bookmark bookmark = (Bookmark) obj;
        return new EqualsBuilder().append(hash, bookmark.hash).isEquals();
    }

    /**
     * Returns the bookmarkID. This is the internal Connotea ID for a bookmark.
     * Useful for debugging.
     * 
     * @return the bookmarkID
     */
    public Long getBookmarkID() {
        return bookmarkID;
    }

    /**
     * Returns the citation, if available. If the bookmark is for an article or
     * book that Connotea can get bibliographic information for, the citation
     * element will be present.
     * 
     * @return the citation
     */
    public Citation getCitation() {
        return citation;
    }

    /**
     * Returns the created date. This is the date the URI was first added to the
     * Connotea database.
     * 
     * @return the created
     */
    public Date getCreated() {
        return new Date(created.getTime());
    }

    /**
     * Returns the firstUser. This is the name of the first user to post this
     * bookmark.
     * 
     * @return the firstUser
     */
    public String getFirstUser() {
        return firstUser;
    }

    /**
     * Returns the hash. This is an MD5 hash of the bookmark's URI. Used as a
     * key by Connotea.
     * 
     * @return the hash
     */
    public String getHash() {
        return hash;
    }

    /**
     * Returns the link.
     * 
     * @return the link
     */
    public Reference getLink() {
        return link;
    }

    /**
     * Returns the postCount. This is the number of times the bookmark has been
     * posted.
     * 
     * @return the postCount
     */
    public Long getPostCount() {
        return postCount;
    }

    /**
     * Returns the postedBy. This is a list of Connotea usernames of users who
     * have this bookmarked this URI.
     * 
     * @return the postedBy
     */
    public List<String> getPostedBy() {
        return unmodifiableList(postedBy);
    }

    /**
     * Returns the tags. This is a list of tags assigned to this URI by all
     * users who've bookmarked it.
     * 
     * @return the tags
     */
    public List<Tag> getTags() {
        return unmodifiableList(tags);
    }

    /**
     * Returns the title. This is the Bookmark title; i.e. the title found in
     * the HTML.
     * 
     * @return the title
     */
    public String getTitle() {
        return title;
    }

    /**
     * Returns the updated date. This is the date the bookmark was last updated.
     * 
     * @return the updated date
     */
    public Date getUpdated() {
        return new Date(updated.getTime());
    }

    /**
     * HashCode is based on the Connotea hash of this bookmark.
     * 
     * @see java.lang.Object#hashCode()
     * @see #getHash()
     */
    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(hash).toHashCode();
    }

    /**
     * Sets the bookmarkID.
     * 
     * @param bookmarkID the bookmarkID to set
     */
    public void setBookmarkID(Long bookmarkID) {
        this.bookmarkID = bookmarkID;
    }

    /**
     * Sets the citation.
     * 
     * @param citation the citation to set
     */
    public void setCitation(Citation citation) {
        this.citation = citation;
    }

    /**
     * Sets the created.
     * 
     * @param created the created to set
     */
    public void setCreated(Date created) {
        this.created = new Date(created.getTime());
    }

    /**
     * Sets the firstUser.
     * 
     * @param firstUser the firstUser to set
     */
    public void setFirstUser(String firstUser) {
        this.firstUser = firstUser;
    }

    /**
     * Sets the hash.
     * 
     * @param hash the hash to set
     */
    public void setHash(String hash) {
        this.hash = hash;
    }

    /**
     * Sets the link.
     * 
     * @param link the link to set
     */
    public void setLink(Reference link) {
        this.link = link;
    }

    /**
     * Sets the postCount.
     * 
     * @param postCount the postCount to set
     */
    public void setPostCount(Long postCount) {
        this.postCount = postCount;
    }

    /**
     * Sets the postedBy.
     * 
     * @param postedBy the postedBy to set
     */
    public void setPostedBy(List<String> postedBy) {
        if (isNotEmpty(postedBy)) {
            this.postedBy.addAll(postedBy);
        }
    }

    /**
     * Sets the tags.
     * 
     * @param tags the tags to set
     */
    public void setTags(List<Tag> tags) {
        if (isNotEmpty(tags)) {
            this.tags.addAll(tags);
        }
    }

    /**
     * Sets the title.
     * 
     * @param title the title to set
     */
    public void setTitle(String title) {
        this.title = title;
    }

    /**
     * Sets the updated.
     * 
     * @param updated the updated to set
     */
    public void setUpdated(Date updated) {
        this.updated = new Date(updated.getTime());
    }

    /**
     * Pretty-prints the current object state.
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return reflectionToString(this);
    }
}
